package com.cn.stepLock.lambda;
import com.google.common.collect.ImmutableList;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

/**
 * @author Aaron
 * @desc
 * @createTime 2020/12/17 11:32
 * @since
 */
public class LambdaUtil {


    public static final Random rand = new Random();
    public static <T> boolean predicate(T t, Predicate<T> predicate){
        return predicate.test(t);
    }

    public static <T,V> boolean predicate(T t, Predicate<T> predicate,V v,Predicate<V> andPredicate){
        return predicate.test(t) && andPredicate.test(v);
    }

    public static <T> boolean predicate(T t, Predicate<T> predicate,Predicate<T> andPredicate){
        return predicate.test(t);
    }

    public static <T> void consumer(T t,Consumer<T> consumer){
        consumer.accept(t);
    }

    public static <T,R> R function(T t, Function<T,R> function){
        return function.apply(t);
    }

    public static <T,R> R ifThenElseWithReturn(T t,Predicate<T> predicate,Function<T,R> then,Function<T,R> elseFunction){
        return predicate(t,predicate)?function(t,then):function(t,elseFunction);
    }

    public static <T,R> R ifThenElseWithReturn(boolean flag,T t,Function<T,R> then,Function<T,R> elseFunction){
        return flag?function(t,then):function(t,elseFunction);
    }

    public static <T,R> R ifThenElseWithReturn(IfThenElseReturn<T,R> ifThenElseReturn){
        return ifThenElseWithReturn(ifThenElseReturn.getInputParameter(),ifThenElseReturn.getPredicate(),ifThenElseReturn.getThen(),ifThenElseReturn.getElseFunction());
    }

    public static <T> void ifThenElse(T t,Predicate<T> predicate,Consumer<T> then,Consumer<T> elseConsumer){
        if (predicate(t,predicate)){
             consumer(t,then);
             return;
        }
        consumer(t,elseConsumer);
    }

    public static <T,U> void ifThen(T t,Predicate<T> predicate,U u,Consumer<U> then){
        ifThen(predicate(t,predicate),u, then);
    }

    public static <U> void ifThen(boolean flag,U u,Consumer<U> then){
        if (flag){
            consumer(u,then);
        }
    }

    public static <T> T ifThen(boolean flag,T t,Function<T,T> then){
        if (flag){
            return then.apply(t);
        }
        return t;
    }

    public static <T,V> V ifThen(boolean flag,T t,Function<T,V> then,V defaultValue){
        if (flag){
            return then.apply(t);
        }
        return defaultValue;
    }

    public static <T,X extends Throwable> void ifThrow(T t,Predicate<T> predicate,Supplier<? extends X> supplier) throws X{
        if (predicate(t,predicate)){
            throw supplier.get();
        }
    }

    public static <X extends Throwable> void ifThrow(boolean flag,Supplier<? extends X> supplier) throws X{
        if (flag){
            throw supplier.get();
        }
    }

    public static <T,U> void ifPresent(T t,Predicate<T> predicate,U u,Consumer<U> then){
        if (predicate(t,predicate)){
            consumer(u,then);
        }
    }

    public static <T> void ifThenElse(IfThenElse<T> ifThenElse){
         ifThenElse(ifThenElse.getInputParameter(),ifThenElse.getPredicate(),ifThenElse.getThen(),ifThenElse.getElseFunction());
    }

    public static <T> List<T> filterList(Collection<T> list,Predicate<T> predicate){
        return list.stream().filter(predicate::test).collect(toList());
    }

    public static <T> Optional<T> findOne(List<T> list,Predicate<T> predicate){
        return filterList(list,predicate).stream().findFirst();
    }

    public static <T,R> List<R> transferList(List<T> list,Function<T,R> function){
        return list.stream().map(function::apply).collect(Collectors.toList());
    }

    public static <T,R> List<R> transferDistinctList(List<T> list,Function<T,R> function){
        return list.stream().map(function::apply).distinct().collect(Collectors.toList());
    }

    public static <K,V> Map<K,List<V>> groupList(final List<V> list,final Function<V,K> function){
        return list.stream().collect(Collectors.groupingBy(function));
    }

    public static <K,V,T> Map<K,List<T>> groupList(final List<V> list,final Function<V,K> keyFunction,final Function<V,T> valueFunction){
        return list.stream().collect(Collectors.groupingBy(keyFunction,Collectors.mapping(valueFunction,Collectors.toList())));
    }

    public static <K,V> Map<K,String> groupListAndMergeValue(final List<V> list,final Function<V,K> keyFunction,final Function<V,String> valueFunction,String delimiter){
        return list.stream().collect(Collectors.groupingBy(keyFunction,Collectors.mapping(valueFunction,Collectors.joining(ifThen(delimiter==null || "".equals(delimiter),delimiter, x->",")))));
    }

    public static <K,V> Map<K,V> groupListToMap(final List<V> list,final Function<V,K> function){
        return list.stream().collect(Collectors.toMap(function,x->x));
    }

    public static <K,V,T> Map<K,V> groupListToMap(final List<T> list,final Function<T,K> keyFunction,Function<T,V> valueFunction){
        return list.stream().collect(Collectors.toMap(keyFunction,valueFunction));
    }

    public static <K,V> List<V> mergeList(List<K> list,final Function< K ,Collection<V>> keyFunction){
        return list.stream().map(keyFunction).flatMap(Collection::parallelStream).collect(toList());
    }

    public static<T,V> List<T> distinctListByKey(List<T> list,Function<T,V> function){
        return groupList(list,function).values().stream().map(x->x.stream().findFirst().get()).collect(toList());
    }

    public static <T,U,R,S> void changeValueBetweenList(final List<T> fromValueList, final Function<T,S> fromKeyFunction, final Function<T,R> fromValueFunction,
                                                        final List<U> toValueList, final Function<U,S> toKeyFunction,final BiConsumer<U,R> biConsumer){
        changeValueFromMap(groupListToMap(fromValueList,fromKeyFunction,fromValueFunction),toValueList, toKeyFunction, biConsumer);
    }

    public static <U, R, S> void changeValueFromMap(Map<S, R> map,List<U> toValueList, Function<U, S> toKeyFunction, BiConsumer<U, R> biConsumer) {
        toValueList.forEach(x->biConsumer.accept(x,map.get(toKeyFunction.apply(x))));
    }

    public static <T,U,R,S> void changeValueBetweenList(ChangeValueBetweenList changeValueBetweenList){
        Map<S,R> map = groupListToMap(changeValueBetweenList.getFromValueList(),changeValueBetweenList.getFromKeyFunction(),changeValueBetweenList.getFromValueFunction());
        changeValueBetweenList.getToValueList()
	        .forEach(x->consumer(map.get(function(x,changeValueBetweenList.getToKeyFunction())),changeValueBetweenList.getToConsumer()));
    }

//    public static void executeUpdateFromDataSource(DataSourceInfo dataSourceInfo, String command){
//        try (Connection connection = DriverManager.getConnection(dataSourceInfo.getUrl(), dataSourceInfo.getUserName(), dataSourceInfo.getPassword())) {
//            connection.createStatement().executeUpdate(command);
//        } catch (SQLException e) {
//            LoggerUtil.publishErrorLogger(LoggerUtil.makeConnectionInfo(dataSourceInfo.getUrl()),e);
//        }
//    }

//    public static String executeUpdateFromDataSourceHehe(DataSourceInfo dataSourceInfo, String command){
//        try (Connection connection = DriverManager.getConnection(dataSourceInfo.getUrl(), dataSourceInfo.getUserName(), dataSourceInfo.getPassword())) {
//            connection.createStatement().executeUpdate(command);
//        } catch (SQLException e) {
//            return e.getMessage();
//        }
//        return "";
//    }
//    public static void executeUpdateFromDataSourceWithoutLog(DataSourceInfo dataSourceInfo, String command){
//        try (Connection connection = DriverManager.getConnection(dataSourceInfo.getUrl(), dataSourceInfo.getUserName(), dataSourceInfo.getPassword())) {
//            connection.createStatement().executeUpdate(command);
//        } catch (SQLException e) {
//            //LoggerUtil.publishErrorLogger(LoggerUtil.makeConnectionInfo(dataSourceInfo.getUrl()),e);
//        }
//    }
//
//    public static <T> Optional<T> getResultFromDataSource(DataSourceInfo dataSourceInfo, String command, Function<ResultSet,T> function){
//        return getResultFromDataSource(dataSourceInfo.getUrl(),dataSourceInfo.getUserName(),dataSourceInfo.getPassword(),command,function);
//    }

    public static <T> Optional<T> getResultFromDataSource(String url,String user,String password, String command, Function<ResultSet,T> function){
        try (Connection connection = DriverManager.getConnection(url, user, password)) {
            ResultSet rs = connection.createStatement().executeQuery(command);
            while (rs.next()) {
                return Optional.ofNullable(function.apply(rs));
            }
        } catch (SQLException e) {
            //LoggerUtil.publishErrorLogger(LoggerUtil.makeConnectionInfo(url),e);
        }
        return Optional.empty();
    }

//    public static <T> List<T> getResultListFromDataSource(DataSourceInfo dataSourceInfo, String command, Function<ResultSet,T> function){
//        List<T> result = new ArrayList<>();
//        try (Connection connection = DriverManager.getConnection(dataSourceInfo.getUrl(), dataSourceInfo.getUserName(), dataSourceInfo.getPassword())) {
//            ResultSet rs = connection.createStatement().executeQuery(command);
//            while (rs.next()) {
//                result.add(function.apply(rs));
//            }
//            return result;
//        } catch (SQLException e) {
//            LoggerUtil.publishErrorLogger(LoggerUtil.makeConnectionInfo(dataSourceInfo.getUrl()),e);
//        }
//        return result;
//    }

    public static <T> List<T> getResultListFromDataSource(String url,String user,String password, String command, Function<ResultSet,T> function){
        List<T> result = new ArrayList<>();
        try (Connection connection = DriverManager.getConnection(url, user, password)) {
            ResultSet rs = connection.createStatement().executeQuery(command);
            while (rs.next()) {
                result.add(function.apply(rs));
            }
            return result;
        } catch (SQLException e) {
            //LoggerUtil.publishErrorLogger(LoggerUtil.makeConnectionInfo(url),e);
        }
        return result;
    }

    public static <T,V> List<T> getRepeatElement(List<T> list,Function<T,V> function){
        return mergeList(filterList(groupList(list,function).values(),x->x.size()>1),x->x);
    }

    public static class ChangeValueBetweenList<T,U,R,S>{
        private final List<T> fromValueList;
        private final Function<T,S> fromKeyFunction;
        private final Function<T,R> fromValueFunction;
        private final List<U> toValueList;
        private final Consumer<R> toConsumer;
        private final Function<U,S> toKeyFunction;

        public ChangeValueBetweenList(List<T> fromValueList, Function<T, S> fromKeyFunction, Function<T, R> fromValueFunction, List<U> toValueList, Consumer<R> toConsumer, Function<U, S> toKeyFunction) {
            this.fromValueList = fromValueList;
            this.fromKeyFunction = fromKeyFunction;
            this.fromValueFunction = fromValueFunction;
            this.toValueList = toValueList;
            this.toConsumer = toConsumer;
            this.toKeyFunction = toKeyFunction;
        }

        public List<T> getFromValueList() {
            return fromValueList;
        }

        public Function<T, S> getFromKeyFunction() {
            return fromKeyFunction;
        }

        public Function<T, R> getFromValueFunction() {
            return fromValueFunction;
        }

        public List<U> getToValueList() {
            return toValueList;
        }

        public Consumer<R> getToConsumer() {
            return toConsumer;
        }

        public Function<U, S> getToKeyFunction() {
            return toKeyFunction;
        }
    }

    public static<T,V> void tryCatch(T t,Consumer<T> tryConsumer,V v,Consumer<V> catchConsumer){
        try {
            tryConsumer.accept(t);
        }catch (Exception e){
            catchConsumer.accept(v);
        }
    }

    public static<T,V> List<T> orderSubList(List<T> list,Comparator<T> comparator,int subSize){
        T element = randomFromList(list);
        List<T> biggerElementList = filterList(list,x->comparator.compare(x,element)>=0);
        List<T> smallerElementList = filterList(list,x->comparator.compare(x,element)<0);
        subSize = subSize - biggerElementList.size();
        if (subSize==0){
            return biggerElementList;
        }
        /**
         * @desc
         */
        if (subSize>0){
            biggerElementList.addAll(orderSubList(smallerElementList,comparator,subSize));
        }
        if (subSize<0){
            return ImmutableList.copyOf(biggerElementList).subList(0,subSize);
        }
        return ImmutableList.of();
    }

    public static<T> T randomFromList(List<T> list){
        return list.get(rand.nextInt(list.size()));
    }

}
